<?php

namespace W3;

use function action_exists;
use function do_action;

/**
 * 组件基类
 *
 * @author edikud
 * @date 2022/10/22
 * @copyright Copyright (c) 2022 W3 (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
abstract class Base
{
    /**
     * widget对象池
     *
     * @access private
     * @var array
     */
    private static $_widgetPool = [];
	
    /**
     * 数据堆栈每一行
     *
     * @var array
     */
    protected $row = [];

    /**
     * 数据堆栈
     *
     * @var array
     */
    public $stack = [];

    /**
     * 当前队列指针顺序值,从1开始
     *
     * @var integer
     */
    public $sequence = 0;

    /**
     * 队列长度
     *
     * @var integer
     */
    public $length = 0;

    /**
     * request对象
     *
     * @var Request
     */
    public $request;

    /**
     * response对象
     *
     * @var Response
     */
    public $response;

    /**
     * 内部对象
     *
     * @var Config
     */
    public $parameter;
	
    /**
     * 构造函数,初始化组件
     *
     * @param mixed $request request对象
     * @param mixed $response response对象
     * @param mixed $params 参数列表
     */
    public function __construct(Request $request, Response $response, $params = [])
    {
        # 设置基类内部对象
        $this->request = $request;
        $this->response = $response;
        $this->parameter = Config::make($params);
		
		# 注册组件
        $this->boot();
    }

    /**
     * 注册组件
     */
    protected function boot()
    {}

    /**
     * 判断是否存在语组件
     * @access public
     * @param string $abstract 组件名称
     * @return bool
     */
    public static function exists(string $alias) : bool
    {
        return isset(self::$_widgetPool[$alias]);
    }

    /**
     * 释放组件
     *
     * @access public
     * @param string $abstract 组件名称
     * @return void
     */
    public static function destory(string $alias)
    {
        if (isset(self::$_widgetPool[$alias])) {
            unset(self::$_widgetPool[$alias]);
        }
    }

	/**
	 * 获取组件
	 *
     * @param string $alias 组件别名
     * @param mixed $params 传递的参数
     * @param mixed $request 前端参数
     * @param boolean $enableResponse 是否允许http回执
	 * @return Container
	 */
    public static function widget(string $alias = null, $params = null, $request = null, $enableResponse = true): Widget
    {
		$pos = strpos($alias, '@');
		$className = $pos ? substr($alias, 0, $pos) : $alias;
			
        if (!isset(self::$_widgetPool[$alias])) {

            /** 初始化request */
            $request = !empty($request) 
			    ? Request::make()->setParams($request)
		        : Request::instance();

            /** 初始化response */
            $response = $enableResponse
			    ? Response::instance()
                : Blank::instance();

            /** 初始化组件 */
            $widget = new $className($request, $response, $params);
			
			$main = $widget->parameter->main;

		    /** 插件接口 */
		    $main && do_action( $main , $widget );
		
			/** 执行 */
            $widget->execute();
			
		    /** 插件接口 后置回调 */
		    $main && do_action( $main . '.after' , $widget );
			
            self::$_widgetPool[$alias] = $widget;
        }

        return self::$_widgetPool[$alias];
    }

    /**
     * execute function.
     *
     * @access public
     * @return void
     */
    public function execute(){}

    /**
     * post事件触发
     *
     * @param boolean $condition 触发条件
     * @return mixed
     */
    public function on(bool $condition)
    {
        if ($condition) {
            return $this;
        } else {
            return Blank::make();
        }
    }

    /**
     * 将类本身赋值
     *
     * @param string $variable 变量名
     * @return self
     */
    public function to(&$variable): Widget
    {
        return $variable = $this;
    }

    /**
     * 格式化解析堆栈内的所有数据
     *
     * @param string $format 数据格式
     */
    public function parse(string $format)
    {
        while ($this->next()) {
            echo preg_replace_callback(
                "/\{([_a-z0-9]+)\}/i",
                function (array $matches) {
                    return $this->{$matches[1]};
                },
                $format
            );
        }
    }

    /**
     * 将每一行的值压入堆栈
     *
     * @param array $value 每一行的值
     * @return array
     */
    public function push(array $value)
    {
        //将行数据按顺序置位
        $this->row = $value;
        $this->length ++;

        $this->stack[] = $value;
        return $value;
    }

    /**
     * 根据余数输出
     *
     * @param mixed ...$args
     * @return void
     */
    public function alt(...$args)
    {
        $num = count($args);
        $split = $this->sequence % $num;
        echo $args[(0 == $split ? $num : $split) - 1];
    }

    /**
     * 返回堆栈是否为空
     *
     * @return boolean
     */
    public function have(): bool
    {
        return !empty($this->stack);
    }

    /**
     * 返回堆栈每一行的值
     *
     * @return array
     */
    public function next()
    {
        $key = key($this->stack);

        if ($key !== null && isset($this->stack[$key])) {
            $this->row = current($this->stack);
            next($this->stack);
            $this->sequence++;
        } else {
            reset($this->stack);
            $this->sequence = 0;
            return false;
        }

        return $this->row;
    }

    /**
     * 魔术函数,用于挂接其它函数
     *
     * @access public
     * @param string $name 函数名
     * @param array $args 函数参数
     * @return void
     */
    public function __call(string $name, array $args)
    {
		# 挂钩: $main@$name 权重大于 call@$name 【注意: $main@$name 挂接所属主体； call@$name 挂接全局】
        if (($this->parameter->main && action_exists($method = $this->parameter->main . '@' . $name))
			|| action_exists($method = 'call@' . $name)) {

            return do_action($method, $this, true, ...$args);
			
        } else {
		    echo $this->{$name};
		}
    }

    /**
     * merge
     *
     * @param array $arguments
     * @access public
     * @return void
     */
    public function parameter(array $arguments = [], bool $isDefault = false)
    {
		if($isDefault){
			$this->parameter->default($arguments);
		}else{
			$this->parameter->merge($arguments);
		}
    }

    /**
     * 魔术函数,用于获取内部变量
     *
     * @access public
     * @param string $name 变量名
     * @return mixed
     */
    public function __get(string $name)
    {
        if (array_key_exists($name, $this->row)) {
            return $this->row[$name];
        } else {
			
			# 挂钩: $main::$name 权重大于 get::$name  【注意: $main::$name 挂接所属主体；get::$name 挂接全局】
            if (($this->parameter->main && action_exists($method = $this->parameter->main . '::' . $name))
			    || action_exists($method = 'get::' . $name)) {
				
                return do_action( $method, $this, true );
				
            } else {
				
                $method = '___' . $name;

                if (method_exists($this, $method)) {
                    return $this->$method();
                }
			}
        }

        return null;
    }

    /**
     * 设定堆栈每一行的值
     *
     * @param string $name 值对应的键值
     * @param mixed $value 相应的值
     * @return void
     */
    public function __set(string $name, $value)
    {
        $this->row[$name] = $value;
    }

    /**
     * 验证堆栈值是否存在
     *
     * @access public
     * @param string $name
     * @return boolean
     */
    public function __isset(string $name)
    {
        return isset($this->row[$name]);
    }
	
    /**
     * 重置为初始状态
     * @return void
     */
    public function reset(): Widget
	{
        $this->row = [];
        $this->length = 0;
        $this->sequence = 0;
        $this->stack = [];
		
		return $this;
    }
}
